#  -*- coding: utf-8 -*-
#  Copyright (c) 2025-2025 Huawei Technologies Co., Ltd.
#  #
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#  #
#  http://www.apache.org/licenses/LICENSE-2.0
#  #
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.   


"""
msmodelslim.utils.logging 模块的单元测试
"""

import logging
from unittest import TestCase

from msmodelslim.utils.logging import (
    logger_setter, get_logger, set_logger_level
)


class TestLoggerSetter(TestCase):
    """测试 logger_setter 装饰器和上下文管理器"""

    def test_decorator_when_decorate_function_with_prefix_then_use_specified_logger(self):
        """测试装饰器功能：当装饰函数并指定前缀时，函数内部应使用指定的logger名称"""

        @logger_setter(prefix="test.module")
        def test_function():
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "test.module")

        test_function()

    def test_decorator_when_decorate_function_without_prefix_then_use_module_name(self):
        """测试装饰器功能：当装饰函数但不指定前缀时，应使用函数所在模块名作为logger名称"""

        @logger_setter()
        def test_function():
            logger = get_logger()
            # 验证logger名称使用当前模块名
            self.assertIn("test_logging", logger.name)

        test_function()

    def test_decorator_when_decorate_class_with_prefix_and_subfix_then_all_methods_use_specified_logger(self):
        """测试装饰器功能：当装饰类并指定前缀和后缀时，类的所有方法都应使用指定的logger名称"""

        @logger_setter(prefix="test.class", subfix="methods")
        class TestClass:
            def method1(self):
                logger = get_logger()
                # 返回logger名称供外部验证
                return logger.name

            @staticmethod
            def static_method():
                logger = get_logger()
                # 返回logger名称供外部验证
                return logger.name

            @classmethod
            def class_method(cls):
                logger = get_logger()
                # 返回logger名称供外部验证
                return logger.name

        # 创建实例并测试方法
        obj = TestClass()
        self.assertEqual(obj.method1(), "test.class.methods")
        self.assertEqual(TestClass.static_method(), "test.class.methods")
        self.assertEqual(TestClass.class_method(), "test.class.methods")

    def test_context_manager_when_use_with_prefix_then_use_specified_logger_and_restore_after_exit(self):
        """测试上下文管理器功能：使用前缀时应在内部使用指定logger，退出后恢复原始logger"""
        # 在上下文管理器外部
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部
        with logger_setter(prefix="test.context"):
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "test.context")

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_context_manager_when_use_with_prefix_and_subfix_then_use_combined_logger_name(self):
        """测试上下文管理器功能：使用前缀和后缀时logger名称应为prefix.subfix格式"""
        # 在上下文管理器外部
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部，使用前缀和后缀
        with logger_setter(prefix="test.app", subfix="database"):
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "test.app.database")

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_context_manager_when_nested_use_then_each_level_has_own_logger_and_restore_correctly(self):
        """测试嵌套上下文管理器：每层应使用自己的logger，退出时正确恢复到上一层的logger"""
        original_logger = get_logger()
        original_name = original_logger.name

        with logger_setter(prefix="outer"):
            outer_logger = get_logger()
            self.assertEqual(outer_logger.name, "outer")

            with logger_setter(prefix="inner", subfix="nested"):
                inner_logger = get_logger()
                self.assertEqual(inner_logger.name, "inner.nested")

            # 验证回到外层
            current_logger = get_logger()
            self.assertEqual(current_logger.name, "outer")

        # 验证回到最外层
        final_logger = get_logger()
        self.assertEqual(final_logger.name, original_name)

    def test_context_manager_when_no_prefix_specified_then_use_calling_module_name(self):
        """测试上下文管理器功能：不指定前缀时应使用调用模块名作为logger名称"""
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部，不指定 prefix
        with logger_setter():
            logger = get_logger()
            # 验证logger名称使用当前模块名
            self.assertIn("test_logging", logger.name)

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_context_manager_when_only_subfix_specified_then_use_calling_module_with_subfix(self):
        """测试上下文管理器功能：只指定后缀时logger名称应为模块名.subfix格式"""
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部，只指定 subfix
        with logger_setter(subfix="test"):
            logger = get_logger()
            # 验证logger名称使用当前模块名.test
            self.assertTrue(logger.name.endswith(".test"))

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_decorator_when_specify_prefix_then_use_specified_prefix(self):
        """测试装饰器功能：指定前缀时应使用指定的前缀作为logger名称"""

        @logger_setter(prefix="msmodelslim.utils.logging")
        def test_function_with_prefix():
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "msmodelslim.utils.logging")

        test_function_with_prefix()

    def test_decorator_when_specify_subfix_then_use_function_module_with_subfix(self):
        """测试装饰器功能：指定后缀时logger名称应为函数模块名.subfix格式"""

        @logger_setter(subfix="custom")
        def test_function_with_subfix():
            logger = get_logger()
            # 验证logger名称使用当前模块名.custom
            self.assertTrue(logger.name.endswith(".custom"))

        test_function_with_subfix()


class TestLoggingFunctions(TestCase):
    """测试其他日志相关函数"""

    def test_get_logger_when_no_name_specified_then_return_default_logger(self):
        """测试get_logger函数：不指定名称时应返回默认logger，指定名称时应返回对应名称的logger"""
        # 测试获取默认 logger
        logger = get_logger()
        self.assertIsInstance(logger, logging.Logger)

        # 测试获取指定名称的 logger
        named_logger = get_logger("test.named")
        self.assertIsInstance(named_logger, logging.Logger)
        self.assertEqual(named_logger.name, "test.named")

    def test_set_logger_level_when_valid_level_then_set_successfully(self):
        """测试set_logger_level函数：设置有效级别时应成功，设置无效级别时应抛出异常"""
        # 测试设置有效的日志级别
        set_logger_level("debug")
        logger = get_logger()
        self.assertEqual(logger.level, logging.DEBUG)

        set_logger_level("info")
        logger = get_logger()
        self.assertEqual(logger.level, logging.INFO)

        set_logger_level("warning")
        logger = get_logger()
        self.assertEqual(logger.level, logging.WARNING)

        set_logger_level("error")
        logger = get_logger()
        self.assertEqual(logger.level, logging.ERROR)

        set_logger_level("critical")
        logger = get_logger()
        self.assertEqual(logger.level, logging.CRITICAL)

        # 测试设置无效的日志级别
        with self.assertRaises(Exception):
            set_logger_level(123)  # 非字符串类型


if __name__ == "__main__":
    import unittest

    unittest.main()
